

package server;

import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
/**
 *
* @author Muhammad Asad Ullah
 */
public class clientHandler extends Thread {

    
    protected Server parentServerObject; //Main Server Instance
    private DataInput In;  //Data Input for Socket
    private DataOutput Out; //Data Output for Socket.
    private Boolean isSuperUser; //If client for this handler is super user.
    private Socket socket;   // Socket for this handler.
    private Boolean keepRunning;  // Whether to keep listerning for clinet's requests.
    private String userName; // clients user name.
    private int clientHandlerId;  // Id of this instance - assigned by Server Instance.
    private protocolParser parser;  //Parser Instance to parse incoming requests.
    private responseBuilder responsebuilder; //to build out going responses

    /**
     *  This Constructor is to be always called by Server Instance.
     * @param s - Server Instance with in which this handler is contained.
     * @param socket - Socket which is connected to Client.
     * @param id  -  an Id is assigned by the Server to this Client Handler
     * @throws IOException - If There is an IO Error .
     */
    public clientHandler(Server s, Socket socket,int id) throws IOException
    {
        this.parser = new protocolParser();
        this.responsebuilder = new responseBuilder();
        this.clientHandlerId = id;
        keepRunning= true;
        this.socket = socket;
        this.parentServerObject = s;
        this.userName="";
        try
        {
            In = new DataInputStream(socket.getInputStream());
            Out = new DataOutputStream(socket.getOutputStream());            
        }
        catch(IOException ioe)
        {
           this.parentServerObject.removeUser(userName, id, "error");
        }
         this.start();
        
        
    }
/**
 *  Asynchronous reques processing and response sending.
 */
    @Override
public void run()
{
        try
        {

        String request = In.readUTF();
        if(request == null || request.length() == 0) throw new Exception();
            if(parser.getOperation(request) != Operation.LOGIN)
            {
                this.send(this.responsebuilder.invalidRequestResponse());
                this.parentServerObject.removeUser(userName,this.getClientHandlerId(),"");

            }
            else
            {
                String [] parameters = parser.getOperationParameters(Operation.LOGIN, request);
                if(this.parentServerObject.AuthenticateUser(parameters))
                {
                    if(parameters[2].compareToIgnoreCase("true") == 0)
                        this.isSuperUser= true;
                    else
                    this.isSuperUser = false;
                    this.userName = parameters[0]; System.out.println("user added = "+ userName );
                    this.send(responsebuilder.LoginResponse(parameters,true));
                    this.parentServerObject.sendtoAll(this.parentServerObject.getUserList());
                    
                }
                else
                {
                    this.parentServerObject.removeUser(this.userName, this.clientHandlerId,
                            "%%Operation=login%%status=fail");
                }
                
            }
        }
        catch(Exception e)
        {
            
             String message = "%%Operation=logout%%"+this.userName+"%%your request caused an inrecoverabel error"+
                         "you will be disconnected";
                 this.parentServerObject.removeUser(userName, this.clientHandlerId,message);
        }
        while(keepRunning)
        {            
            try
            {
              String request = In.readUTF();
              Operation op = parser.getOperation(request);
              String [] parameters = parser.getOperationParameters(op, request);
              switch(op)
              {
                  case LOGOUT:
                  {
                      if(parameters[0].equalsIgnoreCase(this.userName))
                      {
                      this.send(responsebuilder.logoutResponse(parameters));                      
                      this.setKeepRunning((Boolean) false);
                      socket.close();
                      this.parentServerObject.removeUser(userName,this.getClientHandlerId()
                              ,"%%Operation=logout%%success");
                      }
                      else
                      {
                          this.parentServerObject.removeUser(this.userName, this.clientHandlerId, "%%Operation=logout%%success");
                      }
                      break;
                  }
                  case SENDMESSAGE:
                  {
                      if(parameters[0].equalsIgnoreCase(this.userName))
                      {
                      this.parentServerObject.sendtoAll(responsebuilder.messageBroadCast(parameters));
                      this.send("%%Operatin=sendmessage%%status=successful");
                      }
                      else
                      {
                          this.send("%%Operation=sendmessage%%status=fail");
                      }
                      break;
                  }
                  case GETUSERLIST:
                  {
                      this.send(this.parentServerObject.getUserList());
                      break;
                  }
                  case ADDUSER:
                  {
                      if(this.isSuperUser)
                          this.send(responsebuilder.AddUserResponse(parameters, this.parentServerObject.addUser(parameters)));
                      else
                          this.send(responsebuilder.AddUserResponse(parameters, false));
                      break;
                  }
                  case EDITUSER:
                  {
                      if(this.isSuperUser)
                      {
                          if(this.parentServerObject.editUser(parameters))
                          {
                              this.send(responsebuilder.EditUserResponse(parameters,true));
                              this.parentServerObject.sendto(parameters[0], responsebuilder.editedUserWarning(parameters));
                          }
                      }
                      else
                          this.send(responsebuilder.EditUserResponse(parameters, false));
                      break;

                  }
                  case DELETEUSER:
                  {
                      if (this.isSuperUser)
                      {
                         this.send(responsebuilder.deleteUserResponse(parameters, this.parentServerObject.deleteUser(parameters)));
                         this.send(responsebuilder.KickUserResponse(parameters,this.parentServerObject.kickUser(parameters)));

                      }
                      else
                          this.send(responsebuilder.deleteUserResponse(parameters, false));
                      break;
                  }
                  case KICKUSER:
                  {
                      if(this.isSuperUser)
                          this.send(responsebuilder.KickUserResponse(parameters,this.parentServerObject.kickUser(parameters)));
                      else
                          this.send(responsebuilder.KickUserResponse(parameters,false));
                      break;
                  }
                  case INVALIDOPERATION:
                  {
                      this.send("%%Opertaion=unknown");
                  }
              }
              
            }
             catch(Exception ioe)
             {
                 String message = "%%Operation=logout%%"+this.userName+"%%your request caused an inrecoverabel error"+
                         "you will be disconnected";
                 this.parentServerObject.removeUser(userName, this.clientHandlerId,message);
                 
             }


        }


}
   


    /**
     * This Function is called by Server to Terminate this Handler.
     * @param keepRunning the keepRunning to set
     */
    public void setKeepRunning(Boolean keepRunning) {
        this.keepRunning = keepRunning;
    }

    /**
     * Return this Client handlers ID.
     * @return the clientHandlerId
     */
    public int getClientHandlerId() {
        return clientHandlerId;
    }

    /**
     * @param clientHandlerId the clientHandlerId to set
     */
    public void setClientHandlerId(int clientHandlerId) {
        this.clientHandlerId = clientHandlerId;
    }

    /**
     * To release all resources held by this thread.
     */
    public void disPose()
    {
        try
        {
            this.socket.close(); this.In = null;Out = null;
        }catch(Exception ex){}
    }

    

    /**
     * This function provides a synchronizd way for different object to Write data
     * to client of this handler.
     * @param s - String to be send to client of this handler.
     * @return - True upon Successful transmission and false other wise.
     */
    public synchronized boolean send(String s)
    {
     
               try{Out.writeUTF(s); }
            catch(IOException ioe){return false;}
            return true;
    }

    /**
     *
     * @return - User name of Client of this Client handler.
     */
    public String getUserName()
    {
        return this.userName;
    }


}
